// Author: Bjorn Schobben

#include "Poly.h"

Poly::Poly()
{
}

Poly::Poly(const Poly& rhs): points(rhs.points), color(rhs.color)
{
}

Poly::~Poly()
{
}

unsigned int Poly::GetSize() const
{
	return points.size();
}

const Vector& Poly::GetPoint(unsigned int index) const
{
	return points[index];
}

void Poly::AddPoint(const Vector& point)
{
	points.push_back(point);
}

void Poly::SetColor(const Pixel& ncolor)
{
	color = ncolor;
}

const Pixel& Poly::GetColor() const
{
	return color;
}

Poly& Poly::operator=(const Poly& rhs)
{
	points = rhs.points;
	color = rhs.color;

	return *this;
}



// Clip polygon according to clip rectangle
void Poly::Clip(const Rect& clipRect)
{
	// Implementation of Sutherland Hodgman clipping

	const Vector& topLeft = clipRect.GetTopLeft();
	const Vector& bottomRight = clipRect.GetBottomRight();
	int value;
    Vector first, second;
	Pixel colorBackup = color;

	
	for(int edge=0; edge<4; edge++)
	{
	    if(GetSize() < 2)
		{
			return;
		}

		Poly clippedPoly;


		switch(edge)
		{
			case BOTTOMEDGE:
			{
				value = bottomRight.GetIntY();
				break;
			}

			case RIGHTEDGE:
			{
				value = bottomRight.GetIntX();
				break;
			}

			case TOPEDGE:
			{
				value = topLeft.GetIntY();
				break;
			}

			case LEFTEDGE:
			{
				value = topLeft.GetIntX();
				break;
			}
		}
	    
		// Begin with last point
		first = GetPoint(GetSize() - 1);
	    
		for(int i=0; i<GetSize(); i++)
		{
			second = GetPoint(i);
			
			// Both points inside the clipping rectangle
			if (InsideClipRect(first, edge, value) && InsideClipRect(second, edge, value))
			{
				clippedPoly.AddPoint(second);
			}
			else if (InsideClipRect(first, edge, value))  // First point is inside, and second is not
			{
				Vector intersection = GetClipIntersect(first, second, edge, value);
				clippedPoly.AddPoint(intersection);
			}
			else if (InsideClipRect(second, edge, value)) // First point is outside, second is inside
			{
				Vector intersection = GetClipIntersect(first, second, edge, value);
				clippedPoly.AddPoint(intersection);
				clippedPoly.AddPoint(second);
			}
			else // Both points are outside
			{
				// Don't add any points
			}
			
			first = second;
		}

		*this = clippedPoly;
	}


	SetColor(colorBackup);
}


bool Poly::InsideClipRect(const Vector& point, int edge, int value)
{
    switch(edge)
    {
		case BOTTOMEDGE:
			return point.GetIntY() < value;
		case RIGHTEDGE:
			return point.GetIntX() < value;
		case TOPEDGE:
			return point.GetIntY() > value;
		case LEFTEDGE:
			return point.GetIntX() > value;
    }

	return false;
}


Vector Poly::GetClipIntersect(const Vector& first, const Vector& second, int edge, int ivalue)
{
    const Scalar& x0 = first.GetX();
    const Scalar& y0 = first.GetY();
    const Scalar& x1 = second.GetX();
    const Scalar& y1 = second.GetY();
    
    Scalar x, y;

	Scalar value = (Scalar)ivalue;
    
	switch(edge)
	{
		case BOTTOMEDGE:
		case TOPEDGE:
		{
			x = x0 + (x1 - x0) * (value - y0) / (y1-y0);
			y = value;
		}
		break;

		case LEFTEDGE:
		case RIGHTEDGE:
		{
			y = y0 + (y1 - y0) * (value - x0) / (x1-x0);
			x = value;
		}
		break;
	}

	return Vector(x, y);
}

// Add offset vector to each point
void Poly::AddOffset(const Vector& offset)
{
	for (unsigned int i = 0; i < points.size(); i++)
		points[i] += offset;
}